/*  
 * Actipro Software's WPF Docking & MDI / Prism Integration Add-on
 * http://actipro.codeplex.com/
 * 
 * Copyright (c) 2009-2015 Actipro Software LLC. 
 *  
 * This source code is subject to the terms of the Microsoft Public License (Ms-PL). 
 *  
 * Redistribution and use in source and binary forms, with or without modification, 
 * is permitted provided that redistributions of the source code retain the above 
 * copyright notices and this file header. 
 *  
 * Additional copyright notices should be appended to the list above. 
 * 
 * For details, see <http://www.opensource.org/licenses/ms-pl.html>. 
 *  
 * All other rights reserved. 
 *
 */  

using System;
using System.Windows;
using System.Windows.Data;
using ActiproSoftware.Products.Docking.Interop.Prism;
using ActiproSoftware.Windows.Controls.Docking;
using ActiproSoftware.Windows.Prism.Regions.Behaviors;
using Microsoft.Practices.Prism.Regions;

namespace ActiproSoftware.Windows.Prism.Regions {

	/// <summary>
	/// Adapter that creates a new <see cref="Region"/> and binds all the views to the adapted <see cref="DockSite"/>.
	/// </summary>
	public class DockSiteRegionAdapter : RegionAdapterBase<DockSite> {

		/////////////////////////////////////////////////////////////////////////////////////////////////////
		#region OBJECT
		/////////////////////////////////////////////////////////////////////////////////////////////////////

		/// <summary>
		/// Initializes a new instance of the <see cref="DockSiteRegionAdapter"/> class that supports document views.
		/// </summary>
		/// <param name="regionBehaviorFactory">The factory used to create the region behaviors to attach to the created regions.</param>
		public DockSiteRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory)
			: this(regionBehaviorFactory, DockSiteRegionViewType.Both) {
			// No-op
		}


		/// <summary>
		/// Initializes a new instance of the <see cref="DockSiteRegionAdapter"/> class.
		/// </summary>
		/// <param name="regionBehaviorFactory">The factory used to create the region behaviors to attach to the created regions.</param>
		/// <param name="supportedViewType">Type of the views that are supported by the adapter.</param>
		private DockSiteRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory, DockSiteRegionViewType supportedViewType)
			: base(regionBehaviorFactory) {
			this.SupportedViewType = supportedViewType;
		}

		#endregion // OBJECT

		/////////////////////////////////////////////////////////////////////////////////////////////////////
		#region NON-PUBLIC PROCEDURES
		/////////////////////////////////////////////////////////////////////////////////////////////////////

		/// <summary>
		/// Gets the <see cref="DockingWindowItemType"/> associated with the specified item, if any.
		/// </summary>
		/// <param name="item">The item whose docking type should be returned.</param>
		/// <returns>An instance of <see cref="DockingWindowItemType"/>.</returns>
		internal static DockingWindowItemType GetDockingWindowItemType(object item) {
			if (item is DocumentWindow)
				return DockingWindowItemType.Document;
			else if (item is ToolWindow)
				return DockingWindowItemType.Tool;

			IDockingWindowItemTypeProvider provider = item as IDockingWindowItemTypeProvider;
			if (provider != null)
				return provider.GetDockingWindowItemType(item);

			DependencyObject depObj = item as DependencyObject;
			if (depObj != null) {
				provider = DockingWindowItemTypeProvider.GetProvider(depObj);
				if (provider != null)
					return provider.GetDockingWindowItemType(item);

				if (DependencyPropertyHelper.GetValueSource(depObj, DockingWindowItemTypeProvider.ItemTypeProperty).BaseValueSource != BaseValueSource.Default)
					return DockingWindowItemTypeProvider.GetItemType(depObj);

				provider = DockingWindowInitializer.GetInitializer(depObj) as IDockingWindowItemTypeProvider;
				if (provider != null)
					return provider.GetDockingWindowItemType(item);
			}

			return DockingWindowItemType.Document;
		}

		/// <summary>
		/// Gets the <see cref="IDockingWindowInitializer"/> associated with the specified view, if any.
		/// </summary>
		/// <param name="view">The view.</param>
		/// <returns>An instance of <see cref="IDockingWindowInitializer"/>; otherwise, <see langword="null"/>.</returns>
		internal static IDockingWindowInitializer GetDockingWindowInitializer(object view) {
			IDockingWindowInitializer initializer = view as IDockingWindowInitializer;
			if (initializer == null) {
				DependencyObject depObj = view as DependencyObject;
				if (depObj != null)
					initializer = DockingWindowInitializer.GetInitializer(depObj);
			}
			return initializer;
		}

		/// <summary>
		/// Gets the type of the views that are supported by the adapter.
		/// </summary>
		/// <value>The type of the views that are supported by the adapter.</value>
		internal DockSiteRegionViewType SupportedViewType {
			get;
			private set;
		}

		#endregion // NON-PUBLIC PROCEDURES

		/////////////////////////////////////////////////////////////////////////////////////////////////////
		#region PUBLIC PROCEDURES
		/////////////////////////////////////////////////////////////////////////////////////////////////////

		/// <summary>
		/// Adapts a <see cref="DockSite"/> to an <see cref="IRegion"/>.
		/// </summary>
		/// <param name="region">The new region being used.</param>
		/// <param name="regionTarget">The object to adapt.</param>
		protected override void Adapt(IRegion region, DockSite regionTarget) {
			if (region == null)
				throw new ArgumentNullException("region");
			else if (regionTarget == null)
				throw new ArgumentNullException("regionTarget");

			// Verify DocumentItemsSource is not set
			if (this.SupportedViewType != DockSiteRegionViewType.Tool &&
				(regionTarget.DocumentItemsSource != null || BindingOperations.GetBinding(regionTarget, DockSite.DocumentItemsSourceProperty) != null)) {
				throw new InvalidOperationException(SR.GetString(SRName.ExDockSiteHasDocumentItemsSource));
			}

			// Verify ToolItemsSource is not set
			if (this.SupportedViewType != DockSiteRegionViewType.Document &&
				(regionTarget.ToolItemsSource != null || BindingOperations.GetBinding(regionTarget, DockSite.ToolItemsSourceProperty) != null)) {
				throw new InvalidOperationException(SR.GetString(SRName.ExDockSiteHasToolItemsSource));
			}

			// Set the items source properties, as needed
			if (this.SupportedViewType == DockSiteRegionViewType.Document) {
				regionTarget.DocumentItemsSource = region.Views;
			}
			else if (this.SupportedViewType == DockSiteRegionViewType.Tool) {
				regionTarget.ToolItemsSource = region.Views;
			}
			else {
				regionTarget.DocumentItemsSource = new EnumerableView<object>(region.Views,
					(o) => { return GetDockingWindowItemType(o) != DockingWindowItemType.Document; });
				regionTarget.ToolItemsSource = new EnumerableView<object>(region.Views,
					(o) => { return GetDockingWindowItemType(o) != DockingWindowItemType.Tool; });
			}
		}

		/// <summary>
		/// Attaches the behaviors.
		/// </summary>
		/// <param name="region">The region.</param>
		/// <param name="regionTarget">The region target.</param>
		/// <remarks>
		/// This class attaches the base behaviors and an instance of <see cref="DockSiteRegionBehavior"/>.
		/// </remarks>
		protected override void AttachBehaviors(IRegion region, DockSite regionTarget) {
			if (region == null)
				throw new ArgumentNullException("region");

			// Add the behavior that syncs the items source items with the rest of the items
			region.Behaviors.Add(DockSiteRegionBehavior.BehaviorKey, new DockSiteRegionBehavior() {
				HostControl = regionTarget
			});

			// Call base method
			base.AttachBehaviors(region, regionTarget);
		}

		/// <summary>
		/// Creates a new instance of <see cref="Region"/>.
		/// </summary>
		/// <returns>A new instance of <see cref="Region"/>.</returns>
		protected override IRegion CreateRegion() {
			return new DockSiteRegion();
		}

		#endregion // PUBLIC PROCEDURES
	}
}